#include "fbxanimationcontrol.h"
#include <osgAnimation/LinkVisitor>

namespace CoreBase
{
	FbxAnimationControl::FbxAnimationControl()
	{}

	bool FbxAnimationControl::play(const QString& managerid,float starttime,float endtime,float nlooptimes)
	{
		float duration = endtime  - starttime;
		if(duration>0)
		{
			std::map<QString,osg::ref_ptr<osgAnimation::AnimationManagerBase>>::iterator iter = _map.find(managerid);
			if(iter!=_map.end())
			{
				dynamic_cast<AnimationManager*>(_map[managerid].get())->playAnimation(starttime,duration,nlooptimes);
			}
			return true;
		}
		return false;
	}

	bool FbxAnimationControl::stop(const QString& managerid)
	{
		if(_map.find(managerid) != _map.end())
		{
			dynamic_cast<AnimationManager*>(_map[managerid].get())->stopAnimation();
			return true;
		}
		return false;
	}    
	void FbxAnimationControl::stopAll()
	{
		std::map<QString,osg::ref_ptr<osgAnimation::AnimationManagerBase>>::iterator iter = _map.begin();
		for(;iter!=_map.end();iter++)
		{
			dynamic_cast<AnimationManager*>((*iter).second.get())->stopAnimation();
		}
	}

	void FbxAnimationControl::Clear()
	{
		stopAll();
		_map.clear();
	}

	bool FbxAnimationControl::havefbxAnimationCallback(osg::Node* pNode)
	{
		AnimationManagerFinder finder;
		pNode->accept(finder);
		if (finder._am.valid())
			return true;
		else
			return false;
	}

	bool FbxAnimationControl::registerAnimationCallback(const QString& deviceid,osg::Node* pNode)
	{
		if(_map.find(deviceid) == _map.end())
		{
			AnimationManagerFinder finder;
			pNode->accept(finder);
			if (finder._am.valid()) 
			{
				pNode->setUpdateCallback(finder._am.get());
				_map.insert(std::make_pair<QString,osg::ref_ptr<osgAnimation::AnimationManagerBase>>(deviceid,finder._am));
				return true;
			}
		}
		return false;
	}

	AnimationManager::AnimationManager()
		: _lastUpdate(0.0)
	{}

	AnimationManager::AnimationManager(const AnimationManagerBase& b, const osg::CopyOp& copyop) 
		: AnimationManagerBase(b,copyop),_lastUpdate(0.0)
	{}

	AnimationManager::~AnimationManager() 
	{}

	AnimationManager::InAnimation::InAnimation()
	{
		_startTimeOff = 0.0f;
		_loopTimes = 0;
		_duration = 0.0;
		_originalDuration = 0.0;
	}

	AnimationManager::InAnimation::~InAnimation()
	{}

	void AnimationManager::InAnimation::setBindOsgAnimation(osgAnimation::Animation* ani)
	{
		_bindOsgAnimation = ani;
		computeDefaultDuration();
	}

	osg::ref_ptr<osgAnimation::Animation> AnimationManager::InAnimation::getBindOsgAnimation()
	{
		return _bindOsgAnimation;
	}

	void AnimationManager::InAnimation::setStartOff(float offset)
	{
		_startTimeOff = offset;
	}

	void AnimationManager::InAnimation::setLoopTimes(int times)
	{
		_loopTimes = times;
	}

	bool AnimationManager::InAnimation::updataAnimation(float time,int priority)
	{
		int times = (time - _startTime)/_duration;
		float t = fmod((float)(time - _startTime),(float)_duration);
		if(times<_loopTimes)
		{
			t = t+_startTimeOff;
			if(t<=_originalDuration)
			{
				std::vector<osg::ref_ptr<osgAnimation::Channel>> chanvector = _bindOsgAnimation->getChannels();
				std::vector<osg::ref_ptr<osgAnimation::Channel>>::const_iterator chan;
				for( chan=chanvector.begin(); chan!=chanvector.end(); ++chan) 
				{
					(*chan)->update(t,_weight,priority);
				}
			}
			return true;
		}
		return false;
	}

	double AnimationManager::InAnimation::computeDurationFromChannels() const
	{
		double tmin = 1e5;
		double tmax = -1e5;
		std::vector<osg::ref_ptr<osgAnimation::Channel>> chanvector = _bindOsgAnimation->getChannels();
		std::vector<osg::ref_ptr<osgAnimation::Channel>>::const_iterator chan;
		for( chan=chanvector.begin(); chan!=chanvector.end(); chan++ )
		{
			float min = (*chan)->getStartTime();
			if (min < tmin)
				tmin = min;
			float max = (*chan)->getEndTime();
			if (max > tmax)
				tmax = max;
		}
		return tmax-tmin;
	}

	void AnimationManager::InAnimation::computeDefaultDuration()
	{
		_originalDuration = computeDurationFromChannels();
		_duration = _originalDuration;
	}

	void AnimationManager::stopAnimation()
	{
		// loop over all playing animation
		for( InAnimationVector::iterator iterAnim = _animationsPlaying.begin(); iterAnim != _animationsPlaying.end(); ++iterAnim ) 
		{
			(*iterAnim)->resetTargets();
		}
		_animationsPlaying.clear();
	}

	void AnimationManager::playAnimation()
	{
		if(isPlaying())
			stopAnimation();
		for( osgAnimation::AnimationList::const_iterator iterAnim = _animations.begin(); iterAnim != _animations.end(); ++iterAnim ) 
		{
			osg::ref_ptr<InAnimation> htanimation = new AnimationManager::InAnimation();
			htanimation->setBindOsgAnimation((*iterAnim).get());
			htanimation->setStartTime(_lastUpdate);
			htanimation->setWeight(1.0f);
			htanimation->setStartOff(0.0f);
			htanimation->setLoopTimes(999999);
			_animationsPlaying.push_back(htanimation);
		}
	}

	void AnimationManager::playAnimation (float starttime,float duration,int looptimes,int priority,float weight)
	{
		if(isPlaying())
			stopAnimation();
		for( osgAnimation::AnimationList::const_iterator iterAnim = _animations.begin(); iterAnim != _animations.end(); ++iterAnim ) 
		{
			osg::ref_ptr<InAnimation> htanimation = new InAnimation();
			htanimation->setBindOsgAnimation((*iterAnim).get());
			htanimation->setStartTime(_lastUpdate);
			htanimation->setWeight(weight);
			htanimation->setStartOff(starttime);
			htanimation->setLoopTimes(looptimes);
			htanimation->setDuration(duration);
			_animationsPlaying.push_back(htanimation);
		}
	}

	void AnimationManager::update (double time,int priority)
	{
		_lastUpdate = time;
		for (TargetSet::iterator it = _targets.begin(); it != _targets.end(); ++it)
			(*it).get()->reset();
		std::vector<int> toremove;
		for (unsigned int i = 0; i < _animationsPlaying.size(); i++)
		{
			if (!_animationsPlaying[i]->updataAnimation(time, priority)) 
			{
				toremove.push_back(i);
			}
		}
		while (!toremove.empty())
		{
			_animationsPlaying.erase(_animationsPlaying.begin() + toremove.back());
			toremove.pop_back();
		}
	}

	void AnimationManager::update(double t)
	{
		update(t,0);
	}

	bool AnimationManager::isPlaying()
	{
		if(_animationsPlaying.size()>0)
			return true;
		else
			return false;
	}
}